﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace YTPD.Lib
{
  public class Downloader
  {
    private IStateNotifier _stateNotifier;

    public Downloader(IStateNotifier stateNotifier)
    {
      stateNotifier.ThrowIfNull("stateNotifier");

      _stateNotifier = stateNotifier;
    }

    public LocalFlvFile DownloadAndSaveAsTempFile(YoutubeVideo youTubeVideo)
    {
      youTubeVideo.ThrowIfNull("youTubeVideo");

      var localFile = new LocalFlvFile { FileName = GetTempFileNameWithExtension("flv") };

      var webClient = new WebClientImpl();
      webClient.DownloadFile(youTubeVideo.FlvUri, localFile.FileName, _stateNotifier.SetProgress);

      return localFile;
    }

    public static string GetTempFileNameWithExtension(string extension)
    {
      string fileName = Path.GetTempFileName();
      fileName = Path.ChangeExtension(fileName, extension);
      Utilities.TryDeleteFile(fileName);

      return fileName;
    }

    public static string SantizeFileName(string fileName, string extension)
    {
      const char replaceCharacter = '_';
      var cleanFileName = new System.Text.StringBuilder();

      for (int i = 0; i < fileName.Length; i++)
      {
        var filenameCharacter = fileName[i];
        foreach (var c in Path.GetInvalidFileNameChars())
          if (filenameCharacter.Equals(c))
          {
            filenameCharacter = replaceCharacter;
            break;
          }

        cleanFileName.Append(filenameCharacter);
      }

      if (cleanFileName.Length == 0)
      {
        string tempFileName = Path.GetTempFileName();
        cleanFileName.Append(Path.GetFileNameWithoutExtension(tempFileName));
        Utilities.TryDeleteFile(tempFileName);
      }

      cleanFileName.Append("." + extension);
      return cleanFileName.ToString();
    }

    public static string GetFileNameFromVideoWithExtension(YoutubeVideo video, string extension)
    {
      video.ThrowIfNull("video");

      return SantizeFileName(video.Title, extension);
    }

    public IList<LocalAudioFile> AsAudioOnly(string outputDirectory, IList<YoutubeVideo> videos)
    {
      outputDirectory.ThrowIfNull("outputDirectory");
      videos.ThrowIfNull("videos");

      var localAudioFiles = new List<LocalAudioFile>();
      int currentVideoCount = 1;

      foreach (var video in videos)
      {
        if (video == null)
        {
          continue;
        }

        var outputFilePath = Path.Combine(outputDirectory, GetFileNameFromVideoWithExtension(video, "mp3"));
        if (string.IsNullOrEmpty(outputFilePath))
        {
          currentVideoCount++;
          continue;
        }

        if (videos.Count > 1)
        {
          _stateNotifier.SetState("Downloading " + video.Title + " (" + currentVideoCount + " of " + videos.Count + ")");
        }
        else
        {
          _stateNotifier.SetState("Downloading " + video.Title);
        }

        var flvFile = DownloadAndSaveAsTempFile(video);

        if (videos.Count > 1)
        {
          _stateNotifier.SetState("Converting " + video.Title + " to audio. (" + currentVideoCount + " of " + videos.Count + ")");
        }
        else
        {
          _stateNotifier.SetState("Converting " + video.Title + " to audio.");
        }

        var audioFile = FFMpeg.ConvertToAudioFile(flvFile, outputFilePath, _stateNotifier.SetProgress);

        localAudioFiles.Add(audioFile);

        currentVideoCount++;
      }

      CreateM3UPlaylist(localAudioFiles.ToArray(), Path.Combine(outputDirectory, "ytpd-playlist.m3u"));

      return localAudioFiles;
    }

    private void CreateM3UPlaylist(LocalFile[] localFiles, string outputFilePath)
    {
      StringBuilder playlist = new StringBuilder();
      playlist.AppendLine("#EXTM3U");

      foreach (LocalFile file in localFiles)
      {
        playlist.AppendLine(string.Format("#EXTINF:{0},{1}", file.TotalTime, file.FileName.Substring(0, file.FileName.LastIndexOf('.'))));
        playlist.AppendLine(file.FileName);
      }

      File.WriteAllText(outputFilePath, playlist.ToString(), Encoding.ASCII);
    }

    public IList<LocalVideoFile> AsVideo(string outputDirectory, IList<YoutubeVideo> videos)
    {
      outputDirectory.ThrowIfNull("outputDirectory");

      var localVideoFiles = new List<LocalVideoFile>();
      int currentVideoCount = 1;

      foreach (var video in videos)
      {
        if (video == null)
        {
          continue;
        }

        var outputFilePath = Path.Combine(outputDirectory, GetFileNameFromVideoWithExtension(video, "mp4"));
        if (string.IsNullOrEmpty(outputFilePath))
        {
          currentVideoCount++;
          continue;
        }

        if (videos.Count > 1)
        {
          _stateNotifier.SetState("Downloading " + video.Title + " (" + currentVideoCount + " of " + videos.Count + ")");
        }
        else
        {
          _stateNotifier.SetState("Downloading " + video.Title);
        }

        var flvFile = DownloadAndSaveAsTempFile(video);

        if (videos.Count > 1)
        {
          _stateNotifier.SetState("Converting " + video.Title + " to video. (" + currentVideoCount + " of " + videos.Count + ")");
        }
        else
        {
          _stateNotifier.SetState("Converting " + video.Title + " to video.");
        }

        var audioFile = FFMpeg.ConvertToVideoFile(flvFile, outputFilePath, _stateNotifier.SetProgress);

        localVideoFiles.Add(audioFile);

        currentVideoCount++;
      }

      CreateM3UPlaylist(localVideoFiles.ToArray(), Path.Combine(outputDirectory, "ytpd-playlist.m3u"));

      return localVideoFiles;
    }
  }
}
